{-----------------------------------------------------------------
 
  this module contains the functionality to search for the package
  owning a particular system installed file on the filesystem


  (c) 2008-2009 Markus Dittrich 
 
  This program is free software; you can redistribute it 
  and/or modify it under the terms of the GNU General Public 
  License Version 3 as published by the Free Software Foundation. 
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License Version 3 for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 59 Temple Place - Suite 330, 
  Boston, MA 02111-1307, USA.

--------------------------------------------------------------------}


module Drivers.Owner ( display_file_owner ) where


-- imports
import Prelude
import qualified Data.ByteString as B(any, ByteString)
import qualified Data.ByteString.Char8 as BC(pack)
import System.FilePath.Posix((</>), joinPath, splitPath, 
  takeBaseName)


-- local imports
import Parsers.Content(FileType(..), PackageContentItem(..), 
  retrieve_package_content)
import Helpers.ByteString(slashW)
import Helpers.FileIO(retrieve_directory_contents, splitFileNameBS)
import Helpers.PrettyPrint(putColorBStr, Color(..))



-- | main handler for displaying the owners of a supplied file
display_file_owner :: FilePath -> [FilePath] -> IO ()
display_file_owner searchPath paths =

  mapM_ (\path -> do
    retrieve_directory_contents path
    >>= \pkgs -> 
      let 
        categoryName = takeBaseName path 
        searchPathBS = BC.pack $ replace_lib searchPath
      in
        check_category_contents searchPathBS path categoryName pkgs 
        >>= print_owner
  ) paths



-- | function replacing all occurences of lib with lib64 in
-- and arbitrary filepath
replace_lib :: FilePath -> FilePath
replace_lib = joinPath 
              . map (\x -> if x == "lib/" then "lib64/" else x)
              . splitPath



-- | print successfully matched files
print_owner :: [(String,[B.ByteString])] -> IO () 
print_owner matches = 
  
  mapM_ (\(name,fileNames) -> do
    putStr $ name ++ " ["
    mapM_ (putColorBStr Red) fileNames
    putStrLn "]" 
  ) matches


-- | check the file contents of all packages in a category for a
-- a particular file
check_category_contents :: B.ByteString -> FilePath -> String 
                        -> [FilePath] -> IO [(String,[B.ByteString])]
check_category_contents searchPath baseDir categoryName paths =
  
  mapM (check_package searchPath baseDir categoryName) paths
  >>= return . strip_empty

  where
    strip_empty = foldr (\x a -> case x of 
                                Nothing -> a 
                                Just r  -> r:a
                        ) []



-- | look through the contents of a specific package for
-- the name of a file given on the command line
check_package :: B.ByteString 
              -> FilePath 
              -> String 
              -> FilePath 
              -> IO (Maybe (String,[B.ByteString]))
check_package searchName baseDir categoryName packageName =

  (retrieve_package_content $ baseDir </> packageName)
  >>= return . filter (\x -> fileType x == Obj) 
  >>= return . map fileName 
  >>= return . foldl (find_name) []
  >>= \result ->
    case null result of
      True  -> return Nothing
      False -> return $ Just (categoryName </> packageName, result)

  where
    is_path = B.any (== slashW)

    find_name acc n = if is_path searchName 
                      then
                        if n == searchName
                        then n:acc
                        else acc
                      else
                        let
                          (_,y) = splitFileNameBS n
                        in
                          if y == searchName
                            then n:acc
                            else acc

